﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RemoteImaging.Controls
{
    public partial class CanvasUserControl : UserControl
    {
        private bool _isDrawing;
        private Image _backgroundImage;
        public Image BackgroundImage
        {
            get { return _backgroundImage; }
            set
            {
                _backgroundImage = value;
                this.Invalidate();
            }
        }

        private DrawimgMode _mode;
        public DrawimgMode Mode
        {
            get { return _mode; }
            set
            {
                _mode = value;
                this.Invalidate();
            }
        }

        public Line Arrow { get; set; }
        public Rectangle Roi { get; set; }

        public event EventHandler ArrowDrawn;

        public void OnArrowDrawn(EventArgs e)
        {
            EventHandler handler = ArrowDrawn;
            if (handler != null) handler(this, e);
        }

        public event EventHandler RectangleDrawn;

        public void OnRectangleDrawn(EventArgs e)
        {
            EventHandler handler = RectangleDrawn;
            if (handler != null) handler(this, e);
        }

        private Point _start;
        private Point _end;

        public CanvasUserControl()
        {
            InitializeComponent();
            this.DoubleBuffered = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            PaintImage(e.Graphics);
            PaintShapes(e.Graphics);
            PaintDrawing(e.Graphics);
        }

        private void PaintDrawing(Graphics graphics)
        {
            if (_isDrawing)
            {
                switch (Mode)
                {
                    case DrawimgMode.Rectangle:
                        var rec = new Rectangle(_start.X, _start.Y, _end.X - _start.X, _end.Y - _start.Y);
                        PaintRectangle(graphics, rec);
                        break;
                    case DrawimgMode.Arrow:
                        var line = new Line() { End = _end, Start = _start };
                        PaintArrow(graphics, line);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private void PaintShapes(Graphics graphics)
        {
            switch (Mode)
            {
                case DrawimgMode.Rectangle:
                    PaintRectangle(graphics, Roi);
                    break;
                case DrawimgMode.Arrow:
                    PaintArrow(graphics, Arrow);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void PaintRectangle(Graphics graphics, Rectangle rectangle)
        {
            if (rectangle != Rectangle.Empty)
            {
                using (var pen = new Pen(Color.Red, 3))
                {
                    var screenRectangle = ImageToScreenRectangle(rectangle);
                    graphics.DrawRectangle(pen, screenRectangle);
                }

            }
        }

        private void PaintArrow(Graphics graphics, Line line)
        {
            var p1 = ImageToScreenPoint(line.Start);
            var p2 = ImageToScreenPoint(line.End);
            using (var pen = new Pen(Color.Red, 3))
            {

                pen.StartCap = LineCap.RoundAnchor;
                pen.EndCap = LineCap.ArrowAnchor;
                var screenLine = new Line() { End = p2, Start = p1 };
                graphics.DrawLine(pen, screenLine.Start, screenLine.End);
            }


        }

        private Point ScreenToImagePoint(Point point)
        {
            return new Point((int)(point.X / Zoom), (int)(point.Y / Zoom));
        }

        public Rectangle ScreenToImageRectangle(Rectangle screenRectangle)
        {
            var x = screenRectangle.X / Zoom;
            var y = screenRectangle.Y / Zoom;
            var w = screenRectangle.Width / Zoom;
            var h = screenRectangle.Height / Zoom;

            return new Rectangle((int)x, (int)y, (int)w, (int)h);
        }

        public Rectangle ImageToScreenRectangle(Rectangle imageRectangle)
        {
            var x = imageRectangle.X * Zoom;
            var y = imageRectangle.Y * Zoom;
            var w = imageRectangle.Width * Zoom;
            var h = imageRectangle.Height * Zoom;

            return new Rectangle((int)x, (int)y, (int)w, (int)h);
        }

        private Point ImageToScreenPoint(Point point)
        {
            return new Point((int)(point.X * Zoom), (int)(point.Y * Zoom));
        }

        private void PaintImage(Graphics g)
        {
            var img = BackgroundImage;
            if (img != null)
            {
                var zoom = Zoom;
                var w = img.Width * zoom;
                var h = img.Height * zoom;

                g.DrawImage(img, new Rectangle(0, 0, (int)w, (int)h));
            }
        }


        public float Zoom
        {
            get
            {
                if (BackgroundImage != null)
                {
                    var hz = this.ClientSize.Width / (float)BackgroundImage.Width;
                    var wz = this.ClientSize.Height / (float)BackgroundImage.Height;
                    return Math.Min(hz, wz);
                }
                else
                {
                    return 1;
                }


            }
        }

        private void CanvasUserControl_MouseDown(object sender, MouseEventArgs e)
        {
            _isDrawing = true;
            _start = ScreenToImagePoint(e.Location);

        }

        private void CanvasUserControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_isDrawing) return;
            _end = ScreenToImagePoint(e.Location);

            this.Invalidate();
        }

        private void CanvasUserControl_MouseUp(object sender, MouseEventArgs e)
        {

            _isDrawing = false;
            _end = ScreenToImagePoint(e.Location);

            switch (Mode)
            {
                case DrawimgMode.Rectangle:
                    var rectangle = new Rectangle(_start.X, _start.Y, _end.X - _start.X, _end.Y - _start.Y);
                    Roi = rectangle;
                    OnRectangleDrawn(EventArgs.Empty);
                    break;
                case DrawimgMode.Arrow:
                    var l = new Line() { End = _end, Start = _start };
                    Arrow = l;
                    OnArrowDrawn(EventArgs.Empty);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            this.Invalidate();
        }
    }

    public struct Line
    {
        public Point Start { get; set; }
        public Point End { get; set; }
    }

    public enum DrawimgMode
    {
        Rectangle, Arrow
    }
}
